#include "fbo.h"

#include <gl\glew.h>
#include <gl\glfw.h>

#include <cstdio>

FBO::FBO()
{


}

FBO::~FBO()
{
//  glDeleteFramebuffers(1, &fboId);
  //glDeleteRenderbuffers(1, &color_rb);
  //glDeleteBuffers(1, &fboId);
}


void FBO::init(int width, int height)
{
  // generate namespace for the frame buffer, colorbuffer and depthbuffer
  glGenFramebuffers(1, &fboId);
  glBindFramebuffer(GL_FRAMEBUFFER, fboId);

  glGenRenderbuffers(1, &color_rb);
  glBindRenderbuffer(GL_RENDERBUFFER, color_rb);
  glRenderbufferStorage(GL_RENDERBUFFER, GL_RGBA8, width, height);
  // Attach color buffer to FBO
  glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,GL_RENDERBUFFER, color_rb);
	
	hasFBOError();

  // Go back to regular frame buffer rendering
  glBindFramebuffer(GL_FRAMEBUFFER, 0);
}

// from tutorial on fbos http://fabiensanglard.net/shadowmapping/index.php
void FBO::initShadowFBO(int shadowMapWidth, int shadowMapHeight)
{
	// Try to use a texture depth component
	glGenTextures(1, &depth_rb);
	glBindTexture(GL_TEXTURE_2D, depth_rb);

	// GL_LINEAR does not make sense for depth texture. However, next tutorial shows usage of GL_LINEAR and PCF
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);

	// Remove artifact on the edges of the shadowmap
	glTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP );
	glTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP );

	// No need to force GL_DEPTH_COMPONENT24, drivers usually give you the max precision if available
	glTexImage2D( GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT, shadowMapWidth, shadowMapHeight, 0, GL_DEPTH_COMPONENT, GL_UNSIGNED_BYTE, 0);
	glBindTexture(GL_TEXTURE_2D, 0);

	// create a framebuffer object
	glGenFramebuffersEXT(1, &fboId);
	glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, fboId);

	// Instruct openGL that we won't bind a color texture with the currently binded FBO
	glDrawBuffer(GL_NONE);
	glReadBuffer(GL_NONE);

	// attach the texture to FBO depth attachment point
	glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT,GL_TEXTURE_2D, depth_rb, 0);

	hasFBOError();

  // Go back to regular frame buffer rendering
  glBindFramebuffer(GL_FRAMEBUFFER, 0);
}

void FBO::begin()
{
	glBindFramebuffer(GL_FRAMEBUFFER, fboId);
}


void FBO::end()
{
	glBindFramebuffer(GL_FRAMEBUFFER, 0);
}

bool FBO::hasFBOError()
{
	bool retval = true;

	GLenum FBOstatus = glCheckFramebufferStatus(GL_FRAMEBUFFER);
	switch( FBOstatus )
	{
	case GL_FRAMEBUFFER_COMPLETE: retval = false; break;
	case GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT: break;
		printf("FBO fail, Color-renderable formats include GL_RGBA4, "
			"GL_RGB5_A1, and"
			"GL_RGB565."
			"GL_DEPTH_COMPONENT16 is the only"
			"depth-renderable format. "
			"GL_STENCIL_INDEX8 is the only"
			"stencil-renderable format."
			"\n");
	case GL_FRAMEBUFFER_UNSUPPORTED:  printf("FBO fail, format not supported\n"); break;
	case GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT:  printf("FBO fail, incomplete attach\n"); break;
	case GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS_EXT:  printf("FBO format bad dimensions\n"); break;
	default: 
		printf("FBO fail, format not supported\n");
		break;
	}

	return retval;
}